// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_CASCADE_PRIORITY_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_CASCADE_PRIORITY_H_

#include "base/check_op.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/resolver/cascade_origin.h"

namespace blink {

// The origin and importance criteria are evaluated together [1], hence we
// encode both into a single integer which will do the right thing when compared
// to another such encoded integer. See CascadeOrigin for more information on
// how that works.
//
// [1] https://www.w3.org/TR/css-cascade-3/#cascade-origin
inline uint32_t EncodeOriginImportance(CascadeOrigin origin, bool important) {
  uint8_t important_xor = (static_cast<uint8_t>(!important) - 1) & 0xF;
  return static_cast<uint32_t>(origin) ^ important_xor;
}

// Tree order bits are flipped for important declarations to reverse the
// priority [1].
//
// [1] https://drafts.csswg.org/css-scoping/#shadow-cascading
inline uint32_t EncodeTreeOrder(uint16_t tree_order, bool important) {
  uint16_t important_xor = static_cast<uint16_t>(!important) - 1;
  return static_cast<uint32_t>(tree_order) ^ important_xor;
}

// Layer order bits are flipped for important declarations to reverse the
// priority [1].
//
// [1] https://drafts.csswg.org/css-cascade-5/#cascade-layering
inline uint64_t EncodeLayerOrder(uint16_t layer_order, bool important) {
  uint16_t important_xor = static_cast<uint16_t>(!important) - 1;
  return static_cast<uint64_t>(layer_order) ^ important_xor;
}

// The CascadePriority class encapsulates a subset of the cascading criteria
// described by css-cascade [1], and provides a way to compare priorities
// quickly by encoding all the information in a single integer.
//
// It encompasses, from most significant to least significant:
// Origin/importance; tree order, which is a number representing the
// shadow-including tree order [2]; inline style, which is a boolean incidating
// whether the declaration is in the style attribute [3]; layer order, which is
// a number representing the cascade layer order in the origin and tree scope
// [4]; position, which contains the index (or indices) required to lookup a
// declaration in the underlying structure (e.g. a MatchResult); and finally
// generation, which is a monotonically increasing number generated by
// StyleCascade for each call to StyleCascade::Apply.
//
// [1] https://drafts.csswg.org/css-cascade/#cascading
// [2] https://drafts.csswg.org/css-scoping/#shadow-cascading
// [3] https://drafts.csswg.org/css-cascade/#style-attr
// [4] https://drafts.csswg.org/css-cascade-5/#layer-ordering
class CORE_EXPORT CascadePriority {
 public:
  // The declaration is important if this bit is set on the encoded priority.
  static constexpr uint64_t kImportantBit = 19;            // of high_bits_
  static constexpr uint64_t kOriginImportanceOffset = 16;  // of high_bits_
  static constexpr uint64_t kIsInlineStyleOffset = 52;     // of low_bits
  static constexpr uint64_t kLayerOrderOffset = 36;        // of low_bits_
  static constexpr uint64_t kPositionOffset = 4;           // of low_bits_

  static constexpr uint32_t kOriginImportanceMask =
      0xF << kOriginImportanceOffset;                 // of high_bits_
  static constexpr uint32_t kTreeOrderMask = 0xFFFF;  // of high_bits_
  static constexpr uint64_t kLayerOrderMask =
      static_cast<uint64_t>(0xFFFF) << kLayerOrderOffset;  // of low_bits_
  static constexpr uint64_t kPositionMask = static_cast<uint64_t>(0xFFFFFFFF)
                                            << kPositionOffset;  // of low_bits_
  static constexpr uint64_t kGenerationMask = 0xF;               // of low_bits_

  CascadePriority() : low_bits_(0), high_bits_(0) {}
  CascadePriority(CascadeOrigin origin)
      : CascadePriority(origin, false, 0, false, 0, 0) {}
  CascadePriority(CascadeOrigin origin, bool important)
      : CascadePriority(origin, important, 0, false, 0, 0) {}
  CascadePriority(CascadeOrigin origin, bool important, uint16_t tree_order)
      : CascadePriority(origin, important, tree_order, false, 0, 0) {}

  // For an explanation of 'tree_order', see css-scoping:
  // https://drafts.csswg.org/css-scoping/#shadow-cascading
  CascadePriority(CascadeOrigin origin,
                  bool important,
                  uint16_t tree_order,
                  bool is_inline_style,
                  uint16_t layer_order,
                  uint32_t position)
      : CascadePriority(
            static_cast<uint64_t>(position) << kPositionOffset |
                EncodeLayerOrder(layer_order, important) << kLayerOrderOffset |
                static_cast<uint64_t>(is_inline_style) << kIsInlineStyleOffset,
            EncodeTreeOrder(tree_order, important) |
                EncodeOriginImportance(origin, important)
                    << kOriginImportanceOffset) {}
  // See StyleCascade.generation_.
  CascadePriority(CascadePriority o, uint8_t generation)
      : CascadePriority((o.low_bits_ & ~kGenerationMask) | generation,
                        o.high_bits_) {
    DCHECK_LE(generation, kGenerationMask);
  }

  bool IsImportant() const { return (high_bits_ >> kImportantBit) & 1; }
  CascadeOrigin GetOrigin() const {
    uint64_t important_xor =
        (((~high_bits_ >> kImportantBit) & 1) - 1) & kOriginImportanceMask;
    return static_cast<CascadeOrigin>((high_bits_ ^ important_xor) >>
                                      kOriginImportanceOffset);
  }
  bool HasOrigin() const { return GetOrigin() != CascadeOrigin::kNone; }
  uint32_t GetPosition() const {
    return (low_bits_ & kPositionMask) >> kPositionOffset;
  }
  uint8_t GetGeneration() const { return low_bits_ & kGenerationMask; }

  // Returns a CascadePriority that ignores the importance and all sorting
  // criteria below layer order, which allows us to compare if two
  // CascadePriorities belong to the same cascade layer.
  CascadePriority ForLayerComparison() const {
    uint64_t important_xor =
        (((~static_cast<uint64_t>(high_bits_) >> kImportantBit) & 1) - 1);
    uint32_t high_important_xor =
        important_xor & (kOriginImportanceMask | kTreeOrderMask);
    uint32_t high_bits_without_importance = high_bits_ ^ high_important_xor;
    uint64_t low_important_xor = important_xor & kLayerOrderMask;
    uint64_t low_bits_without_importance_position_and_generation =
        (low_bits_ ^ low_important_xor) & ~(kGenerationMask | kPositionMask);
    return CascadePriority(low_bits_without_importance_position_and_generation,
                           high_bits_without_importance);
  }

  bool operator>=(const CascadePriority& o) const {
    return high_bits_ > o.high_bits_ ||
           (high_bits_ == o.high_bits_ && low_bits_ >= o.low_bits_);
  }
  bool operator<(const CascadePriority& o) const {
    return high_bits_ < o.high_bits_ ||
           (high_bits_ == o.high_bits_ && low_bits_ < o.low_bits_);
  }
  bool operator==(const CascadePriority& o) const {
    return high_bits_ == o.high_bits_ && low_bits_ == o.low_bits_;
  }
  bool operator!=(const CascadePriority& o) const {
    return high_bits_ != o.high_bits_ || low_bits_ != o.low_bits_;
  }

 private:
  friend class StyleCascade;
  friend class StyleCascadeTest;

  CascadePriority(uint64_t low_bits, uint32_t high_bits)
      : low_bits_(low_bits), high_bits_(high_bits) {}

  //  Bit  0-3 : generation
  //  Bit  4-35: position
  //  Bit 36-51: layer_order (encoded)
  //  Bit    52: is_inline_style
  uint64_t low_bits_;

  //  Bit  0-15: tree_order (encoded)
  //  Bit 16-23: origin/importance (encoded)
  uint32_t high_bits_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_CASCADE_PRIORITY_H_
